package com.ning.parking.service.impl;

import cn.hutool.core.date.BetweenFormatter;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ning.parking.config.CmdTypeEnum;
import com.ning.parking.config.GlobalVariableUtils;
import com.ning.parking.domain.*;
import com.ning.parking.domain.VO.BillingResult;
import com.ning.parking.domain.VO.BillingTimeVO;
import com.ning.parking.domain.VO.Pagination;
import com.ning.parking.domain.dto.ParkingRecordPageDto;
import com.ning.parking.service.*;
import com.ning.parking.mapper.ParkingRecordMapper;
import com.ning.parking.utils.PublishRoute;
import com.scsonic.common.domain.enter.CarTypeInfoVO;
import com.scsonic.common.enums.StatusEnum;
import com.scsonic.common.enums.parking.ParkingCarTypeEnum;
import com.scsonic.common.enums.parking.ParkingStatusEnum;
import com.scsonic.common.enums.parking.ParkingTypeEnum;
import com.scsonic.common.enums.parklot.DrivewayTypeEnum;
import com.scsonic.common.enums.parklot.GateCmd;
import com.scsonic.common.enums.trade.PayStatusEnum;
import com.scsonic.common.enums.trade.TradeOrderTypeEnum;
import com.scsonic.common.enums.trade.TradePlatformEnum;
import com.scsonic.common.utils.TradeOrderNoUtil;
import com.scsonic.framework.exception.BizException;
import com.scsonic.framework.util.DateUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 *
 */
@Slf4j
@AllArgsConstructor
@Service
public class ParkingRecordServiceImpl extends ServiceImpl<ParkingRecordMapper, ParkingRecord>
    implements ParkingRecordService{
    final ParkingFeeCalculator parkingFeeCalculator;
    final ParkingLotDrivewayInfoService parkingLotDrivewayInfoService;
    final ParkingPayOrderService parkingPayOrderService;
    final ParkingLotBaseInfoService parkingLotBaseInfoService;
    final ParkingLotWhitelistService parkingLotWhitelistService;
    @Override
    public Pagination<ParkingRecord> selectPage(ParkingRecordPageDto pageBase) {
        ArrayList<Object> objects = new ArrayList<>();
        objects.add(ParkingStatusEnum.TO_BE_LEAVE);
        objects.add(ParkingStatusEnum.IN_PARKING);
        Page<ParkingRecord> page = lambdaQuery()
                .like(ObjectUtil.isNotEmpty(pageBase.getCarLicense()), ParkingRecord::getCarLicense, pageBase.getCarLicense())
                .in(pageBase.getInPark() == StatusEnum.YES, ParkingRecord::getParkingStatus,objects )
                .eq(pageBase.getInPark() == StatusEnum.NO, ParkingRecord::getParkingStatus, ParkingStatusEnum.LEAVE)
                .between(ObjectUtil.isNotEmpty(pageBase.getEntStartTime()), ParkingRecord::getEntTime, pageBase.getEntStartTime(), pageBase.getEntEndTime())
                .between(ObjectUtil.isNotEmpty(pageBase.getExitStartTime()), ParkingRecord::getExitTime, pageBase.getExitStartTime(), pageBase.getExitEndTime())
                .orderByDesc(ParkingRecord::getRecordId).page(pageBase.toPage());

        List<ParkingRecord> records = page.getRecords();

        if (records != null){
            for (ParkingRecord record : records) {
                if (record.getExitTime() == null && record.getEntTime() != null) {
                    record.setParkingTime( DateUtil.formatBetween(  DateUtils.date(),record.getEntTime() , BetweenFormatter.Level.SECOND));
                }else if (record.getExitTime() != null && record.getEntTime() != null) {
                    record.setParkingTime( DateUtil.formatBetween( record.getExitTime(), record.getEntTime(), BetweenFormatter.Level.SECOND));
                }
            }
        }

        return Pagination.of(page);
    }


    @Override
    public ParkingRecord findInParkingParkingRecord(String carLicense, Long parkingLotId) {
        ParkingRecord parkingRecord = this.lambdaQuery()
                .eq(ParkingRecord::getCarLicense, carLicense)
                .eq(ParkingRecord::getParkingLotId, parkingLotId)
//                .eq(ParkingRecord::getParkingStatus, ParkingStatusEnum.IN_PARKING)
                .last("limit 1")
                .orderByDesc(ParkingRecord::getEntTime)
                .one();

        if (parkingRecord !=null) {
            if (parkingRecord.getParkingStatus() ==  ParkingStatusEnum.IN_PARKING|| parkingRecord.getParkingStatus() ==  ParkingStatusEnum.TO_BE_LEAVE) {
                return parkingRecord;
            }else {
                return null;
            }
        }

        return parkingRecord;
    }

    @Override
    public BillingResult billing(Long parkingRecordId) {
        ParkingRecord parkingRecord = getById(parkingRecordId);

        if (parkingRecord  == null){
            throw new BizException(parkingRecord+"无停车记录");
        }
        BillingResult result = new BillingResult();
        if (parkingRecord.getPayStatus() == PayStatusEnum.PAY_SUCCESS) {
            Date payTime = parkingRecord.getPayTime();
            if (payTime != null && (DateUtils.date().getTime() - payTime.getTime()) < (15 * 60 *1000)){
                result.setPayableAmount(BigDecimal.ZERO);
                result.setPayAmount(getPayAmount(parkingRecord.getRecordId()));
                result.setTotalAmount(parkingRecord.getAmount());
                result.setPayStatus(PayStatusEnum.PAY_SUCCESS);
                return result;
            }
        }
        return tempBilling(parkingRecord);
    }

    @Override
    public ParkingRecord billing(ParkingRecord parkingRecordDto) {
        String carLicense = parkingRecordDto.getCarLicense();

        List<ParkingStatusEnum> parkingStatusEnums = Arrays.asList(ParkingStatusEnum.IN_PARKING, ParkingStatusEnum.TO_BE_LEAVE);

        ParkingRecord parkingRecord = null;

        if (parkingRecordDto.getRecordId() != null){
            parkingRecord = this.lambdaQuery()
                    .eq(ParkingRecord::getRecordId, parkingRecordDto.getRecordId())
                    .in(ParkingRecord::getParkingStatus,parkingStatusEnums)
                    .last("limit 1")
                    .orderByAsc(ParkingRecord::getEntTime)
                    .one();
        }else if (ObjectUtil.isNotEmpty(parkingRecordDto.getCarLicense()) ){
            parkingRecord = this.lambdaQuery()
                    .eq(ParkingRecord::getCarLicense, carLicense)
                    .in(ParkingRecord::getParkingStatus,parkingStatusEnums)
                    .last("limit 1")
                    .orderByAsc(ParkingRecord::getEntTime)
                    .one();
        }else {
            throw new BizException("计费缺少必要参数 CarLicense OR RecordId");
        }

        if (parkingRecord  == null){
            throw new BizException(parkingRecordDto.getCarLicense()+"无停车记录");
        }

        if (parkingRecord.getPayStatus() == PayStatusEnum.PAY_SUCCESS) {
            Date payTime = parkingRecord.getPayTime();
            if (payTime != null && (DateUtils.date().getTime() - payTime.getTime()) < (15 * 60 *1000)){
                parkingRecord.setPayableAmount(BigDecimal.ZERO);
                parkingRecord.setPayAmount(getPayAmount(parkingRecord.getRecordId()));
                return parkingRecord;
            }
        }

        if (parkingRecordDto.getExitTime() != null) {
            parkingRecord.setExitTime(parkingRecordDto.getExitTime() );
        }else {
            parkingRecord.setExitTime(DateUtils.date());
        }

        if (parkingRecord == null){
            throw new BizException("无停车记录无法计费");
        }

        tempBilling(parkingRecord);
        return parkingRecord;
    }

    @Override
    public BillingResult tempBilling(ParkingRecord record) {

        Date exitTime = record.getExitTime();

        if (exitTime==null){
            record.setExitTime(DateUtils.date());
        }

        BillingTimeVO billingTime = getBillingTime(record);
        BillingResult billingResult = new BillingResult();

        if (billingTime.getParkingCarType() == ParkingCarTypeEnum.COMMON) {
            //调用计费模块
             billingResult = parkingFeeCalculator.tempBilling(billingTime.getEntTime(),billingTime.getExitTime());

            if (billingResult.getPayStatus() == PayStatusEnum.UN_PAY) {
                BigDecimal payAmount = getPayAmount(record.getRecordId());
                if (payAmount != null && payAmount.doubleValue() > 0) {
                    billingResult.setPayAmount(payAmount);
                }
            }
        }
        record.builderBilling(billingResult);
        updateById(record);
        billingResult.setRecordId(record.getRecordId());
        billingResult.setPayStatus(record.getPayStatus());
        billingResult.setPayableAmount(record.getPayableAmount());
        return billingResult;
    }

    private BigDecimal getPayAmount(Long recordId) {
        List<ParkingPayOrder> list = parkingPayOrderService.lambdaQuery().eq(ParkingPayOrder::getRecordId, recordId).list();
        BigDecimal payAmount = BigDecimal.ZERO;
        for (ParkingPayOrder parkingPayOrder : list) {
            BigDecimal amount = parkingPayOrder.getAmount();
            if (parkingPayOrder.getPayStatus()  == PayStatusEnum.PAY_SUCCESS && amount != null) {
                payAmount = payAmount.add(amount);
            }
        }
        return payAmount;
    }

    @Override
    public BillingTimeVO getBillingTime(ParkingRecord record) {

        //在selectWhitelistRecord方法,先查询白名单不存在内置去查询月租
        BillingTimeVO billingTimeVO = parkingLotWhitelistService.selectWhitelistRecord(record);
        if (billingTimeVO != null){
            return billingTimeVO;
        }
        billingTimeVO.setParkingCarType(ParkingCarTypeEnum.COMMON);
        billingTimeVO.setEntTime(record.getEntTime());
        billingTimeVO.setExitTime(record.getExitTime());

        return billingTimeVO;

    }

    @Override
    public void leave(ParkingRecord parkingRecord, ParkingStatusEnum parkingStatusEnum) {
        switch (parkingStatusEnum){
            case LEAVE:
                parkingRecord.setParkingStatus(parkingStatusEnum);
                saveOrUpdate(parkingRecord);
                sendDoorCmd(parkingRecord,GateCmd.CLOSE);
                parkingLotBaseInfoService.exitSpace(parkingRecord.getParkingLotId());
                break;
            case TO_BE_LEAVE:
                parkingRecord.setParkingStatus(parkingStatusEnum);
                saveOrUpdate(parkingRecord);
                break;
        }
        PublishRoute.pushBroadcast(CmdTypeEnum.OUT_CAR,parkingRecord.getExitDrivewayId(),parkingRecord);
    }


    private void sendDoorCmd(ParkingRecord parkingRecord, GateCmd close) {
        log.info("{}通道进行{}指令",parkingRecord.getExitDrivewayId(),close.getDesc());
    }

    @Override
    public ParkingRecord orderPay(ParkingRecord parkingRecord) {
        TradePlatformEnum payChannel = parkingRecord.getPayChannel();

        if (payChannel == null) {
            throw new BizException("支付方式不能为空");
        }

        if (parkingRecord.getPayAmount()!=null && parkingRecord.getPayAmount().doubleValue()<=0){
            throw new BizException("支付金额不能为空");
        }

        String payOrderNo = TradeOrderNoUtil.getPayOrderNo(TradeOrderTypeEnum.PARKING);
        ParkingRecord billing = billing(parkingRecord);

        billing.setPayChannel(payChannel);
        billing.setPayScene(parkingRecord.getPayScene());
        billing.setPayStatus(PayStatusEnum.UN_PAY);
        billing.setPayOrderNo(payOrderNo);

        if ( billing.getPayableAmount().doubleValue() != parkingRecord.getPayAmount().doubleValue()) {
            throw new BizException("计费超时请重新支付");
        }
        ParkingPayOrder parkingPayOrder = new ParkingPayOrder();
        parkingPayOrder.setParkingLot(billing);

        DateTime payTime = DateUtils.date();
        parkingPayOrder.setPayTime(payTime);
        parkingPayOrder.setPayOrderNo(payOrderNo);
        parkingPayOrder.setPayStatus(PayStatusEnum.PAY_SUCCESS);

        if (payChannel == TradePlatformEnum.CASH || payChannel == TradePlatformEnum.FREE){
            ParkingUserInfo user = GlobalVariableUtils.getUser();
            if (user!=null){
                parkingPayOrder.setPayeeId(user.getUserId());
                parkingPayOrder.setPayeeName(user.getUserName());
                parkingPayOrder.setPayeeEmployeeNo(user.getEmployeeNo());
            }
        }
        boolean save = parkingPayOrderService.save(parkingPayOrder);
        if (save){
            billing.setPayStatus(PayStatusEnum.PAY_SUCCESS);
            billing.setPayTime(payTime);
            updateById(billing);
        }
        return billing;
    }

    /**
     * 获取停车记录详情业务层
     * @param recordId
     * @return
     */
    @Override
    public ParkingRecord recordDetails(Long recordId) {
        ParkingRecord record = getById(recordId);
        if (record==null){
            throw new BizException("停车信息输入有误");
        }
        List<ParkingPayOrder> payOrders = parkingPayOrderService
                .lambdaQuery()
                .eq(ParkingPayOrder::getRecordId, recordId)
                .list();
        if (payOrders != null && !payOrders.isEmpty()){
            record.setParkingPayOrders(payOrders);
        }
        return record;
    }

    @Override
    public ParkingRecord getLastCar(Long drivewayId) {
        ParkingLotDrivewayInfo drivewayInfo = parkingLotDrivewayInfoService.getById(drivewayId);
        LambdaQueryChainWrapper<ParkingRecord> parkingRecordLambdaQueryChainWrapper = this.lambdaQuery();
        if (drivewayInfo.getDrivewayType()== DrivewayTypeEnum.EXIT) {
            parkingRecordLambdaQueryChainWrapper
                    .eq(ParkingRecord::getExitDrivewayId,drivewayId)
                    .orderByAsc(ParkingRecord::getExitTime);
        }else {
            parkingRecordLambdaQueryChainWrapper
                    .eq(ParkingRecord::getEntDrivewayId,drivewayId)
                    .orderByAsc(ParkingRecord::getEntTime);
        }
        return parkingRecordLambdaQueryChainWrapper.last("limit 1").one();
    }

    @Override
    public ParkingRecord payment(ParkingRecord parkingRecord) {

        ParkingRecord record = getById(parkingRecord.getRecordId());

        if (record == null){
            throw new BizException("无停车记录");
        }

        if (record.getParkingStatus()== ParkingStatusEnum.LEAVE){
            throw new BizException("已离场无需支付");
        }

        if (TradePlatformEnum.FREE == parkingRecord.getPayChannel()){
            record.setPayStatus(PayStatusEnum.PAY_SUCCESS);
            record.setAmount(BigDecimal.ZERO);
            leave(record,ParkingStatusEnum.LEAVE);
            return record;
        }

        BigDecimal payAmount = parkingRecord.getPayAmount();

        if (payAmount==null){
            throw new BizException("无支付金额");
        }

        record.setPayAmount(payAmount);
        TradePlatformEnum payChannel = parkingRecord.getPayChannel();
        record.setPayChannel(payChannel);

        record = orderPay(record);

        if (record.getPayStatus()== PayStatusEnum.PAY_SUCCESS|| record.getPayStatus()==PayStatusEnum.UN_NEED_PAY) {
            record.setLedShow(record.getCarLicense()+"支付成功祝您一路顺风");
            leave(record,ParkingStatusEnum.LEAVE);
        }else {
            record.setLedShow(record.getCarLicense()+"支付金额跳点请支付剩余余额");
            leave(record,ParkingStatusEnum.TO_BE_LEAVE);
        }

        return record;
    }

    @Override
    public ParkingRecord getRecordByCarlicese(String carLicense) {
        return null;
    }

    @Override
    public void updateCarTypeInfo(ParkingLotMonthlyApply parkingLotMonthlyApply) {
        ParkingRecord inParkingParkingRecord = findInParkingParkingRecord(parkingLotMonthlyApply.getCarLicense(), parkingLotMonthlyApply.getParkingLotId());
        if (inParkingParkingRecord != null) {
            CarTypeInfoVO carTypeInfoVO = CarTypeInfoVO.getDefault();
            carTypeInfoVO.setParkingType(ParkingTypeEnum.MONTHLY);
            carTypeInfoVO.setParkingCarType(ParkingCarTypeEnum.MONTHLY);
            carTypeInfoVO.setCarTypeName(parkingLotMonthlyApply.getMonthlyRuleName());
            carTypeInfoVO.setExceedCar(StatusEnum.NO);
            carTypeInfoVO.setStartTime(parkingLotMonthlyApply.getMonthlyStartDay());
            carTypeInfoVO.setEndTime(parkingLotMonthlyApply.getMonthlyEndDay());
            carTypeInfoVO.setCarTypeId(parkingLotMonthlyApply.getMonthlyId());
            inParkingParkingRecord.builderCarInfo(carTypeInfoVO);
            updateById(inParkingParkingRecord);
        }
    }

    @Override
    public void updateCarTypeInfo(ParkingLotWhitelist parkingLotBaseInfo) {
        ParkingRecord inParkingParkingRecord = findInParkingParkingRecord(parkingLotBaseInfo.getCarLicense(), parkingLotBaseInfo.getParkingLotId());
        if (inParkingParkingRecord != null) {
            CarTypeInfoVO carTypeInfoVO = CarTypeInfoVO.getDefault();
            carTypeInfoVO.setParkingType(ParkingTypeEnum.WHITELIST);
            carTypeInfoVO.setParkingCarType(ParkingCarTypeEnum.WHITELIST);
            carTypeInfoVO.setCarTypeName(parkingLotBaseInfo.getVehicleTypeName());
            carTypeInfoVO.setExceedCar(StatusEnum.NO);
            carTypeInfoVO.setStartTime(parkingLotBaseInfo.getStartTime());
            carTypeInfoVO.setEndTime(parkingLotBaseInfo.getEndTime());
            carTypeInfoVO.setCarTypeId(parkingLotBaseInfo.getWhitelistId());
            inParkingParkingRecord.builderCarInfo(carTypeInfoVO);
            updateById(inParkingParkingRecord);
        }
    }

    @Override
    public void updateCarTypeInfo(ParkingLotBlacklist parkingLotBlacklist) {
        ParkingRecord inParkingParkingRecord = findInParkingParkingRecord(parkingLotBlacklist.getCarLicense(), parkingLotBlacklist.getParkingLotId());

        if (inParkingParkingRecord != null) {
            CarTypeInfoVO carTypeInfoVO = CarTypeInfoVO.getDefault();
            carTypeInfoVO.setParkingType(ParkingTypeEnum.TEMP);
            carTypeInfoVO.setParkingCarType(ParkingCarTypeEnum.BLACKLIST);
            carTypeInfoVO.setCarTypeName(parkingLotBlacklist.getBlacklistName());
            carTypeInfoVO.setExceedCar(StatusEnum.NO);
            carTypeInfoVO.setStartTime(parkingLotBlacklist.getStartTime());
            carTypeInfoVO.setEndTime(parkingLotBlacklist.getEndTime());
            carTypeInfoVO.setCarTypeId(parkingLotBlacklist.getBlacklistId());
            inParkingParkingRecord.builderCarInfo(carTypeInfoVO);
            updateById(inParkingParkingRecord);
        }

    }
}




